/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package common

import (
	"encoding/hex"
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/golang/protobuf/proto"
	"hundsun.com/hsl/hschain/common/crypto"
	"hundsun.com/hsl/hschain/protos"
)

// BlackHoleTransactionPayloadType TODO 后续这一块要删除
type BlackHoleTransactionPayloadType uint32

// BlackHoleTransactionPayloadTypeUnknown 未知的BlackHoleTransactionPayload类型
const BlackHoleTransactionPayloadTypeUnknown BlackHoleTransactionPayloadType = 0

// Namespace 获取账户中在状态库中的命名空间，全局唯一。
func (m *Account) Namespace() string {
	//TODO 待实现
	return ""
}

// ##############VoterNetworkAddress##############
var (
	ipv4Regexp               = regexp.MustCompile("^(25[0-4]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[1-9])[.](25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])[.](25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])[.](25[0-4]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[1-9])$")
	hostRegexp               = regexp.MustCompile("^[a-zA-Z]+[a-zA-Z0-9_.-]+$")
	errInvalidNetworkAddress = errors.New("format of network address must be '<IPv4 address>:<port>' or '<hostname>:<port>'")
	errInvalidNetworkHost    = errors.New("host must be an IPv4 address or a valid hostname")
	errInvalidNetworkPort    = errors.New("port of network address must between [0,65535]")
)

// Parse 将字符串格式的网络地址解析为结构体相应字段
func (m *NetworkAddress) Parse(networkAddress string) error {
	firstIndex := strings.Index(networkAddress, ":")
	if firstIndex == -1 {
		return errInvalidNetworkAddress
	}
	lastIndex := strings.LastIndex(networkAddress, ":")
	if firstIndex != lastIndex {
		return errInvalidNetworkAddress
	}
	host := networkAddress[0:firstIndex]
	if !ipv4Regexp.MatchString(host) && !hostRegexp.MatchString(host) {
		return errInvalidNetworkHost
	}
	portStr := networkAddress[firstIndex+1:]
	port, err := strconv.ParseUint(portStr, 10, 32)
	if err != nil {
		return errInvalidNetworkPort
	}
	if port > 65535 {
		return errInvalidNetworkPort
	}
	m.Host = host
	m.Port = uint32(port)
	return nil
}

// Format 格式化为<host>:<port>形式
func (m *NetworkAddress) Format() string {
	return fmt.Sprintf("%s:%d", m.Host, m.Port)
}

// FormatWithLocalIP 格式化为0.0.0.0:<port>形式
func (m *NetworkAddress) FormatWithLocalIP() string {
	return fmt.Sprintf("0.0.0.0:%d", m.Port)
}

// Validate 验证返回地址是否合法
func (m *NetworkAddress) Validate() error {
	if m.Port > 65535 {
		return errInvalidNetworkPort
	}
	if m.Host == "" || !ipv4Regexp.MatchString(m.Host) && !hostRegexp.MatchString(m.Host) {
		return errInvalidNetworkHost
	}
	return nil
}

// ##############Signature##############

// GetAddress 获取签名者地址
func (m *Signature) GetAddress() protos.Address {
	if len(m.PublicKey) == 0 {
		return ""
	}
	addr, err := protos.ParseAddressFromPublicKeyBytes(m.PublicKey)
	if err != nil {
		return ""
	}
	return addr
}

// ##############Account##############

// GetXXXNamespace get account
func (m *Account) GetXXXNamespace() string {
	return protos.EsIndexAccount
}

// GetXXXKey get InternalAddress
func (m *Account) GetXXXKey() string {
	return m.InternalAddress
}

// GetXXXSearchFields get
func (m *Account) GetXXXSearchFields() map[string]interface{} {
	searchFields := make(map[string]interface{})
	searchFields["type"] = m.Type
	searchFields["balance"] = m.Balance
	searchFields["frozen"] = m.Frozen
	return searchFields
}

// ##############Contract##############

// GetXXXNamespace ...
func (m *Contract) GetXXXNamespace() string {
	return protos.EsIndexContract
}

// GetXXXKey ...
func (m *Contract) GetXXXKey() string {
	if m.ContractId == nil {
		return ""
	}
	return m.Address
}

// GetXXXSearchFields ...
func (m *Contract) GetXXXSearchFields() map[string]interface{} {
	if m.ContractId == nil {
		return nil
	}
	searchFields := make(map[string]interface{})
	searchFields["name"] = m.ContractId.Name
	searchFields["version"] = m.ContractId.Version
	searchFields["blockNumber"] = m.BlockNumber
	return searchFields
}

// ##############ContractEvent##############

// GetXXXNamespace ...
func (m *ContractEvent) GetXXXNamespace() string {
	return protos.EsIndexContractEvent
}

// GetXXXKey ...
func (m *ContractEvent) GetXXXKey() string {
	// eventName.blockNumber.txHash
	return fmt.Sprintf("%s%s%d%s%s", m.Name, protos.EsDocIDSeparator, m.BlockNumber, protos.EsDocIDSeparator, hex.EncodeToString(m.TransactionHash))
}

// GetXXXSearchFields ...
func (m *ContractEvent) GetXXXSearchFields() map[string]interface{} {
	if m.ContractId == nil {
		return nil
	}
	searchFields := make(map[string]interface{})
	searchFields["eventName"] = m.Name
	searchFields["contractAddress"] = m.ContractAddress
	searchFields["txHash"] = hex.EncodeToString(m.TransactionHash)
	searchFields["blockNumber"] = m.BlockNumber
	searchFields["contractName"] = m.ContractId.Name
	searchFields["contractVersion"] = m.ContractId.Version
	searchFields["param1"] = m.IndexedParamFirst
	searchFields["param2"] = m.IndexedParamSecond
	searchFields["param3"] = m.IndexedParamThird
	return searchFields
}

// ##############ContractStatus##############

// GetXXXNamespace ...
func (m *ContractStatus) GetXXXNamespace() string {
	return protos.EsIndexContractStatus
}

// GetXXXKey ...
func (m *ContractStatus) GetXXXKey() string {
	if len(m.Address) < 1 || len(m.Namespace) < 1 {
		return ""
	}
	// address.namespace.key
	return fmt.Sprintf("%s%s%s%s%s", m.Address, protos.EsDocIDSeparator, m.Namespace, protos.EsDocIDSeparator, m.Key)
}

// GetXXXSearchFields ...
func (m *ContractStatus) GetXXXSearchFields() map[string]interface{} {
	searchFields := make(map[string]interface{})
	searchFields["address"] = m.Address
	searchFields["namespace"] = m.Namespace
	searchFields["key"] = m.Key
	return searchFields
}

// ##############NetworkChainAccount##############

// Validate ...
func (m *NetworkChainAccount) Validate() error {
	err := (protos.Address(m.AccountAddress)).Validate()
	if err != nil {
		return err
	}
	if m.PeerID == "" {
		return errors.New("network peer id cannot be empty")
	}
	return ValidatePeerID(m.PeerID)
}

// ##############BalanceAlloc##############

// Validate ...
func (m *BalanceAlloc) Validate() error {
	err := (protos.Address(m.Address)).Validate()
	if err != nil {
		return err
	}
	return nil
}

// ##############ContractVersion##############

// GetXXXNamespace ...
func (m *ContractVersion) GetXXXNamespace() string {
	return protos.EsIndexContractVersion
}

// GetXXXKey ...
func (m *ContractVersion) GetXXXKey() string {
	return m.Name
}

// GetXXXSearchFields ...
func (m *ContractVersion) GetXXXSearchFields() map[string]interface{} {
	searchFields := make(map[string]interface{})
	searchFields["name"] = m.Name
	return searchFields
}

// ValidatePeerID ...
func ValidatePeerID(peerID string) error {

	return nil
}

// Hash Router hash
func (r *Router) Hash() []byte {
	v, err := proto.Marshal(r)
	if err != nil {
		panic(err)
	}
	hashBytes, err := crypto.Hash(v)
	if err != nil {
		panic(err)
	}
	return hashBytes
}

// Validate ...
func (m *Miner) Validate() error {
	err := (protos.Address(m.Address)).Validate()
	if err != nil {
		return err
	}
	if m.PeerID == "" {
		return errors.New("network peer id cannot be empty")
	}
	return ValidatePeerID(m.PeerID)
}
